package com.xuecheng.auth.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;

import javax.annotation.Resource;

/**
 * @author 勾新杰
 * @version 1.0
 * @description 授权服务器配置
 * @date 2022/9/26 22:25
 */
@Configuration
@EnableAuthorizationServer // 认证服务器，主要职责是处理用户的身份验证和授权请求，并颁发访问令牌（Access Token）
public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

    /**
     * 根据名称注入自定义的令牌服务，这个自定义的令牌服务在隔壁TokenConfig中
     */
    @Resource(name = "authorizationServerTokenServicesCustom")
    private AuthorizationServerTokenServices authorizationServerTokenServices;

    /**
     * 认证管理器
     */
    private final AuthenticationManager authenticationManager;

    /**
     * 构造器注入认证管理器
     *
     * @param authenticationManager 认证管理器
     */
    @Autowired
    public AuthorizationServer(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    /**
     * 这里是做一个oauth第三方登录的管理，
     * 不过我们当前项目是作为像微信那样的第三方网站，别人的网站作为请求我们网站授权的那一方。
     *
     * @param clients 注册表，用于存储合法的客户端网站，就是微信的appid的总集合
     * @throws Exception 抛出异常
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        clients
                // 使用内存来存储所有的合法客户端，客户端之间用and()连接，
                // 实际中应该用基于数据库的构建，security框架也提供了：JdbcClientDetailsService，构造器传入数据库类DataSource 即可
                .inMemory()

                // 合法的客户端的唯一id也就是主键，对应微信的appid（合法网站的唯一id）
                .withClient("XcWebApp")
                // 这里就是客户端的密码，对应微信的appsecret（给合法网站分配的密码）
                // 前端会传密码的明文，如果配置了加密，存的是加密后的内容（不管是基于内存还是数据库）
                .secret(new BCryptPasswordEncoder().encode("XcWebApp"))
                // 该客户端允许访问的资源ids，能访问哪些资源
                .resourceIds("xuecheng-plus")
                // 定义允许的授权类型authorization_code: 授权码模式。password: 密码模式。client_credentials: 客户端凭证模式。implicit: 隐式授权模式。refresh_token: 刷新令牌模式。
                // authorization_code: 授权码模式：确保 /oauth/authorize 和 /oauth/token 端点已启用，配置 redirectUris，用于授权服务器重定向用户。
                // 隐式授权模式（implicit）：确保 /oauth/authorize 端点已启用。配置 redirectUris，用于授权服务器重定向用户。
                // 刷新令牌模式（refresh_token）：确保 /oauth/token 端点支持 refresh_token 授权类型。
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                // 允许的授权范围，对应微信的scope（权限范围）
                .scopes("all")
                // 设置是否自动批准授权请求，默认为 false，表示需要用户手动确认。
                .autoApprove(false)
                // 设置重定向URI，用于授权码模式和隐式授权模式。
                // 注意：这是一个白名单，在配置客户端的时候需要指定白名单内容存到数据库或者内存中
                // 客户端请求的 redirect_uri 必须在白名单中，否则授权服务器会拒绝请求。
                // 所以只需要将合法的白名单中的域名映射到本机的hosts文件里面，就能请求微信的授权码模式，并成功获取授权码
                .redirectUris("http://www.51xuecheng.cn")

                // 用and（）连接多个客户端
                .and()

                // 客户端2
                .withClient("website1")
                .secret(new BCryptPasswordEncoder().encode("website1"))
                .resourceIds("xuecheng-plus")
                .authorizedGrantTypes("authorization_code", "password", "client_credentials", "implicit", "refresh_token")
                .scopes("all")
                .autoApprove(false)
                .redirectUris("http://www.51xuecheng.cn");
    }

    /**
     * 配置认证管理器和令牌服务
     * @param endpoints the endpoints configurer
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                // 配置认证管理器
                .authenticationManager(authenticationManager)
                // 配置自定义的令牌服务，用的security oauth2实现的jwt令牌
                .tokenServices(authorizationServerTokenServices)
                // 允许post提交
                .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }

    // 令牌端点的安全配置
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        security.tokenKeyAccess("permitAll()")                    // oauth/token_key是公开
                .checkTokenAccess("permitAll()")                  // oauth/check_token公开
                .allowFormAuthenticationForClients();             // 表单认证（申请令牌）
    }
}
